﻿using System;
using System.Drawing;
using System.IO;
using OpenTK;
using OpenTK.Graphics.OpenGL;

namespace Chimp.Core
{
    public class ChimpEffect
    {
        private readonly int _Fragment;
        private readonly int _Program;
        private readonly int _Vertex;
        private int _Geo;

        public ChimpEffect(string vertPath, string fragPath)
        {
            VertexPath = vertPath;
            FragmentPath = fragPath;
            _Program = CreateProgram();
            if (vertPath != "" && vertPath != null)
            {
                _Vertex = CreateShader(vertPath, ShaderType.VertexShader);
                HasVertex = true;
            }
            if (fragPath != "" && fragPath != null)
            {
                _Fragment = CreateShader(fragPath, ShaderType.FragmentShader);
                HasFragment = true;
            }

            HasGeo = false;


            AttachShaders();

            LinkEffect();
        }

        public bool HasGeo { get; private set; }
        public bool HasVertex { get; private set; }
        public bool HasFragment { get; private set; }
        public string VertexPath { get; set; }
        public string FragmentPath { get; set; }

        ~ChimpEffect()
        {
            /*
            if (HasGeo)
            {
                GL.DetachShader(_Program, _Geo);
                GL.DeleteShader(_Geo);
            }

            if (HasVertex)
            {
                GL.DetachShader(_Program, _Vertex);
                GL.DeleteShader(_Vertex);
            }
            if (HasFragment)
            {
                GL.DetachShader(_Program, _Fragment);
                GL.DeleteShader(_Fragment);
            }
            GL.DeleteProgram(_Program);
            */
        }

        public void LinkEffect()
        {
            GL.LinkProgram(_Program);
        }

        public void AttachShaders()
        {
            if (HasGeo) GL.AttachShader(_Program, _Geo);
            if (HasVertex) GL.AttachShader(_Program, _Vertex);
            if (HasFragment) GL.AttachShader(_Program, _Fragment);
        }

        public void SetInt(string name, int val)
        {
            GL.Uniform1(GL.GetUniformLocation(_Program, name), val);
        }

        public void SetFloat(string name, float val)
        {
            GL.Uniform1(GL.GetUniformLocation(_Program, name), val);
        }

        public void SetFloats(String name, float[] vals)
        {
            GL.Uniform1(GL.GetUniformLocation(_Program, name), vals.Length, vals);
        }

        public void SetVec2(string name, Vector2 val)
        {
            GL.Uniform2(GL.GetUniformLocation(_Program, name), val);
        }

        public void SetVec2s(string name, Vector2[] vals)
        {
            var tmp = new float[vals.Length*2];
            int index = 0;
            foreach (Vector2 v in vals)
            {
                tmp[index] = v.X;
                tmp[index + 1] = v.Y;
                index += 2;
            }
            GL.Uniform1(GL.GetUniformLocation(_Program, name), tmp.Length, tmp);

            tmp = null;
        }

        public void SetVec3(string name, Vector3 val)
        {
            GL.Uniform3(GL.GetUniformLocation(_Program, name), val);
        }

        public void SetVec4(string name, Vector4 val)
        {
            GL.Uniform4(GL.GetUniformLocation(_Program, name), val);
        }

        public void SetMat4(string name, Matrix4 val, bool transpose)
        {
            GL.UniformMatrix4(GL.GetUniformLocation(_Program, name), transpose, ref val);
        }

        public void SetColor(string name, Color val)
        {
            SetVec4(name, new Vector4(val.R/255.0f, val.G/255.0f, val.B/255.0f, val.A/255.0f));
        }

        public void Use()
        {
            GL.UseProgram(_Program);
        }

        public void Fin()
        {
            GL.UseProgram(0);
        }

        public static int CreateProgram()
        {
            int id = GL.CreateProgram();
            return id;
        }

        public static int CreateShader(string path, ShaderType type)
        {
            int id = GL.CreateShader(type);

            var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            TextReader tr = new StreamReader(fs);

            GL.ShaderSource(id, @tr.ReadToEnd());

            GL.CompileShader(id);

            int status;
            GL.GetShader(id, ShaderParameter.CompileStatus, out @status);

            if (status == (int) All.False)
            {
                string err = string.Empty;

                GL.GetShaderInfoLog(id, out err);
                ChimpDebug.LogError("Effect Compile Error:" + err + "\nShaderType:" + type, "XenoEffect.CreateShader");
            }

            return id;
        }
    }
}